/*
 *  Copyright 2001-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package ar.uba.fi.posgrado.economy.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ar.uba.fi.posgrado.common.dao.DAOException;
import ar.uba.fi.posgrado.common.dao.NoSuchEntityException;
import ar.uba.fi.posgrado.common.exception.MissingArgumentException;
import ar.uba.fi.posgrado.common.exception.PosgradoException;
import ar.uba.fi.posgrado.common.exception.PosgradoRuntimeException;
import ar.uba.fi.posgrado.common.exception.RepeatedNameException;
import ar.uba.fi.posgrado.common.model.User;
import ar.uba.fi.posgrado.common.util.CommonUtils;
import ar.uba.fi.posgrado.common.util.CriterionBuilder;
import ar.uba.fi.posgrado.common.util.ExpressionOperator;
import ar.uba.fi.posgrado.economy.dao.BenefitDAO;
import ar.uba.fi.posgrado.economy.model.Benefit;
import ar.uba.fi.posgrado.economy.model.BenefitStatus;
import ar.uba.fi.posgrado.economy.service.BenefitService;

/**
 * Implementacion de {@link BenefitService benefit service}
 * 
 * @author nicolas.gonzalez
 * @see BenefitService
 */
public class BenefitServiceImpl implements BenefitService {

	/** Logger */
	private static Logger logger = Logger.getLogger(BenefitServiceImpl.class);

	private final Double ZERO = 0d;
    private BenefitDAO benefitDAO;

    public BenefitDAO getBenefitDAO() {
        return benefitDAO;
    }

    public void setBenefitDAO(BenefitDAO benefitDAO) {
        this.benefitDAO = benefitDAO;
    }
	
	
	public BenefitServiceImpl() {
	}
	
    /** 
	 * @see ar.uba.fi.posgrado.economy.service.BenefitService#executeOnlineJobs(ar.uba.fi.posgrado.common.model.User)
	 */
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public void executeOnlineJobs(User user) throws PosgradoException {
		logger.info("Por ejecutar los servicios de activacion de beneficios" +
		" y desactivacion de los mismos en forma online ...");

		if (!user.hasAdminRole()) {
			throw new PosgradoException("El usuario: " + user + " no es administrador. " +
										"No puede ejecutar los servicios online");
		}

		this.activateBenefits();
		this.deActivateBenefits();
		
		logger.info("Servicios de activacion de beneficios" +
				" y desactivacion de los mismos en forma online finalizado ...");
	}

	/**
     * @see ar.uba.fi.posgrado.economy.service.BenefitService#activateBenefits()
     */
    @Transactional(readOnly = false, propagation = Propagation.MANDATORY)
    public List<Benefit> activateBenefits() throws PosgradoException {
        /* Busca los q no estan activas (Inactivas) y tienen la fecha de 
         * 'vigencia desde' mayor o igual (si un dia la maquina se apaga entonces es x eso q 
         * se usa mayor) a hoy y ademas la fecha de 'expiracion' mayor a hoy
         * Las modifica a todas en el sentido de cambiarle el estado a Activas
         */
        List<Benefit> response = new ArrayList<Benefit>();
        try {
            Date today = new Date();
            CriterionBuilder builder = new CriterionBuilder();
            builder.add("status", BenefitStatus.Inactive, ExpressionOperator.Equals);
            builder.add("validFrom", today, ExpressionOperator.LowerEqualsThan);
            // Esta podria estar de mas, pero es una validacion q funciona...
            builder.add("validTo", today, ExpressionOperator.GreaterThan);
            logger.info("Buscando beneficios a activar con estado: " + BenefitStatus.Inactive.toStringValue() +
                        ", fecha de vigencia anterior o igual a: " + CommonUtils.formatDate(today) + 
                        " y fecha de expiracion mayor a : " + CommonUtils.formatDate(today));
            
            List<Benefit> benefitsFound = this.benefitDAO.findByCriterions(builder.toCriterions());
            logger.info("# de beneficios a activar encontrados: " + benefitsFound.size());
            
            if (logger.isDebugEnabled()) {
                logger.debug("Por activar cada uno de los beneficios encontrados ...");
            }

            // Se iteran las deudas y se las activa
            for (Benefit benefit : benefitsFound) {
                benefit.setStatus(BenefitStatus.Active);
                benefit = this.update(benefit);
                
                response.add(benefit);
            }
            logger.info("Todos los beneficios activados.");
        } catch (DAOException e) {
            throw new PosgradoException("Excepcion buscando beneficios a activar", e);
        }
        
        return response;
    }

    /**
     * @see ar.uba.fi.posgrado.economy.service.BenefitService#deActivateBenefits()
     */
    @Transactional(readOnly = false, propagation = Propagation.MANDATORY)
    public List<Benefit> deActivateBenefits() throws PosgradoException {
        /* Busca los q tienen la fecha de 
         * 'expriracion' menor a hoy.
         * Los modifica a todos en el sentido de cambiarle el estado a Inactivo
         */
        List<Benefit> response = new ArrayList<Benefit>();
        try {
            Date today = new Date();
            CriterionBuilder builder = new CriterionBuilder();
            builder.add("status", BenefitStatus.Active, ExpressionOperator.Equals);
            builder.add("validTo", today, ExpressionOperator.LowerThan);
            logger.info("Buscando beneficios a desactivar con estado: " + BenefitStatus.Active.toStringValue() +
                        " y fecha de expiracion mayor a : " + CommonUtils.formatDate(today));
            
            List<Benefit> benefitsFound = this.benefitDAO.findByCriterions(builder.toCriterions());
            logger.info("# de beneficios a desactivar encontrados: " + benefitsFound.size());
            
            if (logger.isDebugEnabled()) {
                logger.debug("Por desactivar cada uno de los beneficios encontrados ...");
            }

            // Se iteran las deudas y se las activa
            for (Benefit benefit : benefitsFound) {
                benefit.setStatus(BenefitStatus.Inactive);
                benefit = this.update(benefit);
                
                response.add(benefit);
            }
            logger.info("Todos los beneficios desactivados.");
        } catch (DAOException e) {
            throw new PosgradoException("Excepcion buscando beneficios a desactivar", e);
        }
        
        return response;
    }

    /**
	 * @see ar.uba.fi.posgrado.economy.service.BenefitService#create(ar.uba.fi.posgrado.economy.model.Benefit)
	 */
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	public void create(Benefit benefit) throws MissingArgumentException,
			PosgradoException {
		if ((benefit.getValue().compareTo(ZERO) <= 0)
				&& (benefit.getPercentage().compareTo(ZERO) <= 0)) {
			throw new MissingArgumentException(
					"Valor o Porcentaje tienen que ser "
							+ "distintos de cero y mayores que cero");
		}
		/* Analiza si no existe un beneficio con la misma descripcion. En ese caso
		 * arroja una RepeatedInstanceException */
        try {
            // Puede ser q el nombre de usuario exista
            this.getBenefitDAO().findByDescription(benefit.getDescription());

            throw new RepeatedNameException(
                    "El beneficio con descripcion "
                            + benefit.getDescription()
                            + " no es un beneficio valido. Ya existe un beneficio con"
                            + " esa descripcion.");
        } catch (NoSuchEntityException e) {
            // Si entro aca es xq no existia x lo cual esta bien
        }
		try {
			this.getBenefitDAO().save(benefit);
            logger.info("Beneficio: " + benefit + " creado con exito");
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion al crear el beneficio: "
					+ benefit, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.economy.service.BenefitService#delete(ar.uba.fi.posgrado.economy.model.Benefit)
	 */
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	public void delete(Benefit benefit) throws PosgradoRuntimeException,
			PosgradoException {
		try {
			this.getBenefitDAO().remove(benefit);
			logger.info("Beneficio: " + benefit + ", eliminado con exito");
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion eliminando el benefit: "
					+ benefit, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.economy.service.BenefitService#getAll()
	 */
	@Transactional(readOnly = true)
	public List<Benefit> getAll() throws PosgradoRuntimeException,
			PosgradoException {
		try {
			List<Benefit> response = this.getBenefitDAO().findAll();
			logger.info("Beneficios encontrados: " + response.size());
			return response;
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion buscando beneficios", e);
		}
	}
	
	
	
	
    /**
     * @see ar.uba.fi.posgrado.economy.service.BenefitService#getBenefitsByStatus(ar.uba.fi.posgrado.common.model.Benefit.BenefitStatus)
     */
    @Transactional(readOnly = true)
    public List<Benefit> getBenefitsByStatus(BenefitStatus status)
            throws PosgradoRuntimeException, PosgradoException {
        try {
            CriterionBuilder builder = new CriterionBuilder();
            builder.add("status", status, ExpressionOperator.Equals);
            
            List<Benefit> response = this.benefitDAO.findByCriterions(builder.toCriterions());
            
            logger.info(response.size() + " beneficios encontrados para status: " + status.toString());
            return response;
        } catch (DAOException e) {
            throw new PosgradoException("Excepcion buscando beneficios con status especifico", e);
        }
    }

    /**
	 * @see ar.uba.fi.posgrado.economy.service.BenefitService#getById(java.lang.Long)
	 */
	@Transactional(readOnly = true)
	public Benefit getById(Long benefitId) throws PosgradoRuntimeException,
			PosgradoException {
		try {
			Benefit response = (Benefit) this.getBenefitDAO().findById(
					benefitId);
			logger.info("Beneficio con Id: " + benefitId
					+ ", encontrado con exito");
			return response;
		} catch (DAOException e) {
			throw new PosgradoException(
					"Excepcion buscando el Beneficio con Id: " + benefitId, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.GenericService#update(java.lang.Object)
	 */
    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	public Benefit update(Benefit benefit) throws PosgradoRuntimeException,
			PosgradoException {
        if ((benefit.getValue().compareTo(ZERO) <= 0)
                && (benefit.getPercentage().compareTo(ZERO) <= 0)) {
            throw new MissingArgumentException(
                    "Valor o Porcentaje tienen que ser "
                            + "distintos de cero y mayores que cero");
        }
        try {
            this.getBenefitDAO().update(benefit);
        } catch (DAOException e) {
            throw new PosgradoException("Excepcion al modificar el beneficio: "
                    + benefit, e);
        }
	    
		return benefit;
	}

    /**
     * @see ar.uba.fi.posgrado.economy.service.BenefitService#getBenegitsByIds(java.util.List)
     */
    @Transactional(readOnly = true)
    public List<Benefit> getBenegitsByIds(List<Long> benefitIds)
            throws PosgradoRuntimeException, PosgradoException {
        List<Benefit> response = new ArrayList<Benefit>();
        
        for (Long benefitId : benefitIds) {
            response.add(this.getById(benefitId));
        }
        
        return response;
    }

	/** 
	 * @see ar.uba.fi.posgrado.common.service.GenericService#filter(java.util.List, ar.uba.fi.posgrado.common.model.User)
	 */
	public List<Benefit> filter(List<Benefit> list, User user)
			throws PosgradoRuntimeException, PosgradoException {
		// Se muestran todos los beneficios
		return list;
	}
    
    

}
